#!/usr/bin/env python
# -*- coding: utf-8 -*-

from shared.helper import render_to_response
from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect
from django.core.urlresolvers import reverse

from documents.models import Faculty, Module, ModuleTerm, Lecturer,\
    ModulePart, Document, Item
from documents.forms import DocumentForm, ModuleTermForm, ModulePartForm, ModuleForm, FacultyForm
from django.core.mail.message import EmailMessage
from django.contrib.auth.models import User
from django.db import models
from usermanagement.decorators import object_permission_required
from documents.helper import natsort
from django.db.models.aggregates import Max
from documents.itemtype import ItemType

@login_required
def index(request):
    """
    The index view provides all Faculties
    """
    
    faculties = Faculty.objects.order_by('name')
    return render_to_response(request, 'documents/index.html', {'faculties': faculties})

@login_required
def add_document_item(request, module_part_id, module_term_id):
    """
    Asks for the kind of Item to add and then opens the right form
    """
    pass

@login_required
def add_item(request, document_id):
    """
    Asks for the kind of Item to add and then opens the right form
    """
    pass

@login_required
def delete_item(request, item_id):
    """
    Redirects to the right delete form
    """
    item = Item.objects.get(id = item_id)
    
    return redirect(item.item_type.delete_view(kwargs={ 'item_id': item_id }))

@login_required
def change_item(request, item_id):
    """
    Redirects to the right change form
    """
    item = Item.objects.get(id = item_id)
    
    return redirect(item.item_type.change_view(kwargs={ 'item_id': item_id }))
    
    
@login_required
def modulelist(request, faculty_id):
    """
    The modulelist view provides all modules(->Lecuters->Semester) for a faculty
    I decided that this is the only way to access the Documents because this is the
    way people will search what is important for them.
    """
    
    faculty = Faculty.objects.get(id=faculty_id)
    modules = faculty.module_set.order_by('name')
    
    item_list = []
    
    # get all lecturers for one module and his/her terms
    # it is nested: Module->Lecturer->Semester
    for module in modules:
        
        #retrieve ModuleTerms for the Module
        module_terms = module.moduleterm_set.all()
        
        documents = Document.objects.none()
        
        for module_term in module_terms:
            documents = documents | module_term.document_set.all()
            
        items = Item.objects.none()
        for document in documents:
            items = items | document.item_set.filter(activated = True)
        
        last_date = items.aggregate(Max('datetime'))['datetime__max']
    
        # get a list of distinct Lecturer objects of this Module
        # for this we need to retrieve all ModuleTerms of the Module
        # then get the values for the lecturer, make them distinct and map
        # an Object getter over it.
        lecturers = map(lambda x: Lecturer.objects.get(id=x['lecturer']), module_terms.values('lecturer').distinct())
        
        lecturer_terms = []
        
        # get all semesters for one lecturer
        for lecturer in lecturers:
            lecturer_terms.append((lecturer, module_terms.filter(lecturer=lecturer)))
        
        # append the tuple of a module and a set (eliminated duplicates) of lecturers to the item_list
        item_list.append((module, last_date, lecturer_terms))
        
    return render_to_response(request, 'documents/modulelist.html', {'item_list': item_list, 'faculty': faculty})
    
@login_required
def module(request, module_term_id):
    """
    The module view shows one ModuleTerm (a Module in one Semester and Lecturer)
    with the ModuleParts (categories) of a Module and show the corresponding
    Documents.
    """
    
    # get Module context
    module_term = ModuleTerm.objects.get(id=module_term_id)

    module = module_term.module
    
    lecturer = module_term.lecturer
    semester = module_term.semester
        
    # get Moduleparts
    module_parts = module.modulepart_set.order_by('name')

    # build list of items nested this way: Part->Document->Item
    item_list = []
    for module_part in module_parts:
        documents = module_part.document_set.filter(module_term__lecturer=lecturer,
                                                    module_term__semester=semester) \
                                                    .order_by('name')
        
        document_list = []
        for document in documents:
            # filter out documents of documentname
            items = document.item_set.filter(activated = True).order_by('user__username', 'item_category')
            
            last_date = items.aggregate(Max('datetime'))['datetime__max']
            
            # append the tuple of the name and the documents of this name, module_part
            document_list.append((document, last_date, items))
                    
        natsort(document_list, lambda (x, y, z): x.name);
        item_list.append((module_part, document_list))
        
    return render_to_response(request,
                              'documents/module.html',
                              {'item_list': item_list, 'module_term': module_term, 'item_types': ItemType.item_types})
    



@login_required
def add_document(request, module_part_id, module_term_id):
    """
    The add_document view lets us add a new document
    """

    # Get Document context
    module_part = ModulePart.objects.get(id = module_part_id)
    module_term = ModuleTerm.objects.get(id = module_term_id)
    
    instance = Document(
                            module_part = module_part,
                            module_term = module_term,
                            user = request.user
                       )
    
    # If the form has already been filled in
    if request.method == 'POST':
        
        form = DocumentForm(request.POST, instance = instance)
        if form.is_valid(): # All validation rules pass
            document = form.save()
            document_link = reverse('documents.views.module', kwargs={ 'module_term_id': module_term.id })

            __send_document_email(document, request.build_absolute_uri(document_link))

            return redirect(document_link)
    
    # If the form has yet to be filled in
    else:
        form = DocumentForm(instance = instance)
        
    return render_to_response(
                                request,
                                'documents/add_document.html',
                                {
                                    'form': form,
                                    'module_part': module_part,
                                    'module_term': module_term
                                }
                             )
    
@object_permission_required('documents.delete_document', Document, 'document_id')
def delete_document(request, document_id):
    document = Document.objects.get(id = document_id)
    
    if request.method == 'POST':
        module_term = document.module_term
        document.delete()
        return redirect(reverse('documents.views.module', kwargs={ 'module_term_id': module_term.id }))
    
    return render_to_response(
                                request,
                                'documents/delete_document.html',
                                {
                                    'document': document
                                }
                             )
            
@object_permission_required('documents.change_document', Document, 'document_id')
def change_document(request, document_id):
    """
    The change_document view lets us change a document
    """

    # Get Document context
    instance = Document.objects.get(id = document_id)
    
    # If the form has already been filled in
    if request.method == 'POST':
        form = DocumentForm(request.POST, instance = instance)
        if form.is_valid(): # All validation rules pass
            form.save()
            
            return redirect(reverse('documents.views.module', kwargs={ 'module_term_id': instance.module_term.id }))
    
    # If the form has yet to be filled in
    else:
        form = DocumentForm(instance = instance)
        
    return render_to_response(
                                request,
                                'documents/change_document.html',
                                {
                                    'form': form,
                                    'document': instance
                                }
                             )

@login_required
def add_faculty(request):
    """
    The add_faculty view lets us add a new faculty
    """

    instance = Faculty(user = request.user)

    # If the form has already been filled in
    if request.method == 'POST':
        form = FacultyForm(request.POST, instance = instance)
        if form.is_valid(): # All validation rules pass
            faculty = form.save()
            
            faculty_link = reverse('documents.views.modulelist', kwargs={ 'faculty_id': faculty.id })
            
            __send_faculty_email(faculty, request.build_absolute_uri(faculty_link))
            
            return redirect(faculty_link)
    
    # If the form has yet to be filled in
    else:
        form = FacultyForm()
        
    return render_to_response(
                                request,
                                'documents/add_faculty.html',
                                {
                                    'form': form,
                                }
                             )
    
@object_permission_required('documents.delete_faculty', Faculty, 'faculty_id')
def delete_faculty(request, faculty_id):
    faculty = Faculty.objects.get(id = faculty_id)
    
    if request.method == 'POST':
        faculty.delete()
        return redirect(reverse('documents.views.index'))
    
    return render_to_response(
                                request,
                                'documents/delete_faculty.html',
                                {
                                    'faculty': faculty
                                }
                             )


@login_required
def add_module(request, faculty_id):
    """
    The add_module adds a new Module to a faculty
    """

    # Get module context
    faculty = Faculty.objects.get(id = faculty_id)
    
    instance = Module(
                        faculty = faculty,
                        user = request.user
                     )
    
    
    # If the form has already been filled in
    if request.method == 'POST':
        form = ModuleForm(request.POST, instance = instance)
        if form.is_valid(): # All validation rules pass
            module = form.save()

            module_link = reverse('documents.views.modulelist', kwargs={ 'faculty_id': faculty.id })

            __send_module_email(module, request.build_absolute_uri(module_link))

            return redirect(module_link)
    
    # If the form has yet to be filled in
    else:
        form = ModuleForm(instance = instance)
        
    return render_to_response(
                                request,
                                'documents/add_module.html',
                                {
                                    'form': form,
                                    'faculty': faculty
                                }
                             )
    

@object_permission_required('documents.delete_module', Module, 'module_id')
def delete_module(request, module_id):
    module = Module.objects.get(id = module_id)
    
    if request.method == 'POST':
        faculty = module.faculty
        module.delete()
        return redirect(reverse('documents.views.modulelist', kwargs={ 'faculty_id': faculty.id }))
    
    return render_to_response(
                                request,
                                'documents/delete_module.html',
                                {
                                    'module': module
                                }
                             )
    
@object_permission_required('documents.change_module', Module, 'module_id')
def change_module(request, module_id):
    """
    The change view changes a Module
    """

    # Get module
    instance = Module.objects.get(id = module_id)
    
    
    # If the form has already been filled in
    if request.method == 'POST':
        form = ModuleForm(request.POST, instance = instance)
        if form.is_valid(): # All validation rules pass
            form.save()

            return redirect(reverse('documents.views.modulelist', kwargs={ 'faculty_id': instance.faculty.id }))
    
    # If the form has yet to be filled in
    else:
        form = ModuleForm(instance = instance)
        
    return render_to_response(
                                request,
                                'documents/change_module.html',
                                {
                                    'form': form,
                                    'module': instance
                                }
                             )

@login_required
def add_module_term(request, module_id):
    """
    The add_module_term view adds a new ModuleTerm to a Module
    This means you add a new Lecturer-Semester pair for a Module
    """
    
    # Get module_term context
    module = Module.objects.get(id = module_id)
    
    instance = ModuleTerm(
                            module = module,
                            user = request.user
                         )
    
    # If the form has already been filled in
    if request.method == 'POST':
        form = ModuleTermForm(request.POST, instance = instance)
        if form.is_valid(): # All validation rules pass
            first_name, last_name = form.cleaned_data['lecturer']
            semester = form.cleaned_data['semester']
            
            try:
                lecturer = Lecturer.objects.get(first_name__iexact = first_name, last_name__iexact = last_name)
            except Lecturer.DoesNotExist:
                lecturer = Lecturer.objects.create(first_name = first_name, last_name = last_name, user = request.user)
            
            module_term = ModuleTerm.objects.create(module = module, semester = semester, user = request.user, lecturer = lecturer)
            
            module_term.save()

            return redirect(reverse('documents.views.module', kwargs={ 'module_term_id': module_term.id }))
    
    # If the form has yet to be filled in
    else:
        form = ModuleTermForm(instance = instance)
        
    return render_to_response(
                                request,
                                'documents/add_module_term.html',
                                {
                                    'form': form,
                                    'module': module
                                }
                             )
    
@object_permission_required('documents.delete_moduleterm', ModuleTerm, 'module_term_id')
def delete_module_term(request, module_term_id):
    module_term = ModuleTerm.objects.get(id = module_term_id)
    
    if request.method == 'POST':
        module = module_term.module
        module_term.delete()
        
        return redirect(reverse('documents.views.modulelist', kwargs={ 'faculty_id': module.faculty.id }))
    
    return render_to_response(
                                request,
                                'documents/delete_module_term.html',
                                {
                                    'module_term': module_term
                                }
                             )
    
@object_permission_required('documents.change_moduleterm', ModuleTerm, 'module_term_id')
def change_module_term(request, module_term_id):
    """
    The change_module_term view changes a ModuleTerm
    This means you change a new Lecturer-Semester pair for a Module
    """

    # Get module_term context
    instance = ModuleTerm.objects.get(id = module_term_id)
    
    
    # If the form has already been filled in
    if request.method == 'POST':
        form = ModuleTermForm(request.POST, instance = instance)
        if form.is_valid(): # All validation rules pass
            (first_name, last_name) = form.cleaned_data['lecturer']
            semester = form.cleaned_data['semester']
            
            # look if the Lecturer exists - if not: create it
            try:
                lecturer = Lecturer.objects.get(first_name__iexact = first_name, last_name__iexact = last_name)
            except Lecturer.DoesNotExist:
                lecturer = Lecturer.objects.create(first_name = first_name, last_name = last_name, user = request.user)
            
            instance.lecturer = lecturer
            
            instance.semester = semester
            
            instance.save()
            
            return redirect(reverse('documents.views.module', kwargs={ 'module_term_id': instance.id }))
    
    # If the form has yet to be filled in
    else:
        form = ModuleTermForm(instance = instance, initial = {'lecturer': instance.lecturer.__unicode__()})
        
    return render_to_response(
                                request,
                                'documents/change_module_term.html',
                                {
                                    'form': form,
                                    'module_term': instance
                                }
                             )

@login_required
def add_module_part(request, module_term_id):
    """
    The add_module_part view adds a new ModulePart to a Module which is a new category
    """

    # Get module_part context
    module_term = ModuleTerm.objects.get(id = module_term_id)
    module = module_term.module
    
    instance = ModulePart(
                            module = module,
                            user = request.user
                         )
    
    
    # If the form has already been filled in
    if request.method == 'POST':
        form = ModulePartForm(request.POST, instance = instance)
        if form.is_valid(): # All validation rules pass
            form.save()

            return redirect(reverse('documents.views.module', kwargs={ 'module_term_id': module_term.id }))
    
    # If the form has yet to be filled in
    else:
        form = ModulePartForm(instance = instance)
        
    return render_to_response(
                                request,
                                'documents/add_module_part.html',
                                {
                                    'form': form,
                                    'module_term': module_term
                                }
                             )
    
@object_permission_required('documents.delete_modulepart', ModulePart, 'module_part_id')
def delete_module_part(request, module_part_id, module_term_id):
    """
    The delete_module_part view deals with deleting a ModulePart
    """
    module_part = ModulePart.objects.get(id = module_part_id)
    
    if request.method == 'POST':
        module = module_part.module
        module_part.delete()
        return redirect(reverse('documents.views.module', kwargs={ 'module_term_id': module_term_id }))
    
    return render_to_response(
                                request,
                                'documents/delete_module_part.html',
                                {
                                    'module_part': module_part,
                                    'module_term_id': module_term_id
                                }
                             )

@object_permission_required('documents.change_modulepart', ModulePart, 'module_part_id')
def change_module_part(request, module_part_id, module_term_id):
    """
    The change_module_part view changes a ModulePart
    """

    # Get module_part context
    instance = ModulePart.objects.get(id = module_part_id)
    
    
    # If the form has already been filled in
    if request.method == 'POST':
        form = ModulePartForm(request.POST, instance = instance)
        if form.is_valid(): # All validation rules pass
            form.save()

            return redirect(reverse('documents.views.module', kwargs={ 'module_term_id': module_term_id }))
    
    # If the form has yet to be filled in
    else:
        form = ModulePartForm(instance = instance)
        
    return render_to_response(
                                request,
                                'documents/change_module_part.html',
                                {
                                    'form': form,
                                    'module_part': instance,
                                    'module_term_id': module_term_id
                                }
                             )

def __get_mail_receivers():
    """
    Extract the staff from the
    """
    
    result = []
    staff = User.objects.filter(is_staff = True)
    for user in staff:
        result.append(user.email)
    
    return result

def __send_faculty_email(faculty, faculty_link):
    faculty_mail = EmailMessage('Neuer Fachbereich auf campusshare.org', # Header
        'Hallo!\n Es gibt den neuen Fachbereich "' + faculty.name + '" auf campusshare.org.\n' \
        'Er wurde erstellt von ' + faculty.user.username + "\n" + faculty_link,
        'TomLottermann@googlemail.com', # From
        __get_mail_receivers() # To
    )
    
    faculty_mail.send()
    
def __send_module_email(module, module_link):
    module_mail = EmailMessage('Neues Modul auf campusshare.org', # Header
        'Hallo!\n Es gibt das neue Modul "' + module.name + '" auf campusshare.org.\n' \
        'Es wurde erstellt von ' + module.user.username + "\n" + module_link,
        'TomLottermann@googlemail.com', # From
        __get_mail_receivers() # To
    )
    
    module_mail.send()
    
def __send_document_email(document, document_link):
    document_mail = EmailMessage('Neues Dokument auf campusshare.org', # Header
        'Hallo!\n Es gibt die neue Dokumentanfrage zu "' + document.name + '" auf campusshare.org.\n' \
        'Sie wurde erstellt von ' + document.user.username + "\n" + document_link,
        'TomLottermann@googlemail.com', # From
        __get_mail_receivers() # To
    )
    
    document_mail.send()
    
def __send_item_email(item):
    item_mail = EmailMessage('Neues Item auf campusshare.org', # Header
        'Hallo!\n Es gibt das neue Item mit dem Kommentar "' + item.comment + '" auf campusshare.org.\n' \
        'http://www.campusshare.org/' + reverse('upload.views.upload', kwargs={ 'item_id': item.id }), # TODO: somehow construct an abstract URL
        'TomLottermann@googlemail.com', # From
        __get_mail_receivers() # To
    )
    
    item_mail.send()
    
def send_item_handler(instance, **kwargs):
        """
        Sends a mail if a item changes/is added
        """
        if isinstance(instance, Item):
            if kwargs['created']:
                __send_item_email(instance)
    
# register pre_delete connector in order for the ModuleTerm to detect a
# cascaded/non-cascaded delete because overriding delete() is not enough
models.signals.post_save.connect(send_item_handler)